parser grammar Javasint;

/*
//Solucion del if con backtrack=false:
if_statement: IF^ ifexp ifbloque (elseif)?;
ifexp: LPARENTHESIS! expression RPARENTHESIS!; 
ifbloque: (bloque_stats | statement); 
elseif: ELSE^ (bloque_stats | statement);

*/


options {
  language = Java;
  tokenVocab = Javalex;
  backtrack = true; //para resolver ambiguedades
  //k = 3;
}

/*@header {
	import java.io.BufferedWriter;
	import java.io.FileWriter;
}*/

@members {
	SymTable tabla = new SymTable();
	boolean tipoFunc = false; //para saber si funcion devuelve tipo o no
	StringBuilder codigoSAM = new StringBuilder(); // Contiene el codigo SAM que se va generando dinamicamente
	
  protected void mismatch(IntStream input, int ttype, BitSet follow)
    throws RecognitionException
  {
    throw new MismatchedTokenException(ttype, input);
  }
}

@rulecatch { 
  catch (RecognitionException e) {
    throw e;
  }
}

//program: data_def* function_def* EOF ;

program: def def* EOF { codigoSAM.append("\nHALT"); System.out.println("\nFINAL: \n" + tabla); } ;
def: (INT? ID LPARENTHESIS) => function_def | data_def;

data_def:
        INT d1=declarator { tabla.lookUp($d1.name, EntryTS.VAR, 0, Tokens.INT); codigoSAM.append("\nPUSH 1"); }  
        (COMMA d2=declarator { tabla.lookUp($d2.name, EntryTS.VAR, 0, Tokens.INT); codigoSAM.append("\nPUSH 1"); } )* SEMICOLON 
        | LONG d1=declarator { tabla.lookUp($d1.name, EntryTS.VAR, 0, Tokens.LONG); codigoSAM.append("\nPUSH 2"); }  
        (COMMA d2=declarator { tabla.lookUp($d2.name, EntryTS.VAR, 0, Tokens.LONG); codigoSAM.append("\nPUSH 2"); })* SEMICOLON;
         
declarator returns [String name]: 
    id=ID {$declarator.name = $id.getText();};
    
function_def: 
        { System.out.println("\nANTES DE FUNCION:\n" + tabla);
         tabla.blkPush(); } 
        (INT {tipoFunc = true;} )? 
        h=function_header
        { codigoSAM.append("\n\n" + $h.name + ":");
         if (tipoFunc) 
            tabla.lookUp($h.name, EntryTS.FUNC, 0, Tokens.INT); 
          else
            tabla.lookUp($h.name, EntryTS.FUNC, 0, 0); 
        }
        
        function_body 
        { System.out.println("\nAL FINAL DE LA FUNCION: \n" + tabla);
          tabla.blkPop(); 
          //System.out.println("\nDESPUES DE POP: \n" + tabla);
          if (tipoFunc)
             codigoSAM.append("\nRETURN (1) 1"); //funcion retorna algo
          else 
             codigoSAM.append("\nRETURN (0) 1"); 
            
          tipoFunc = false; //para las siguientes funciones 
        } ;
        
function_header returns [String name] : 
            declarator parameter_list { $function_header.name = $declarator.name;} ;
            
parameter_list: LPARENTHESIS identifier_list? RPARENTHESIS parameter_decl*;

identifier_list: id1=ID { tabla.lookUp($id1.getText(), EntryTS.PARAM, 0, 0); } 
             (COMMA id2=ID { tabla.lookUp($id2.getText(), EntryTS.PARAM, 0, 0); } )*;

parameter_decl: INT d1=declarator { tabla.lookUp($d1.name, EntryTS.PARAM, 0, Tokens.INT); codigoSAM.append("\nPUSH 1"); }
               (COMMA d2=declarator { tabla.lookUp($d1.name, EntryTS.PARAM, 0, Tokens.INT); codigoSAM.append("\nPUSH 1"); })* SEMICOLON;
               
               
function_body: LCURLY data_def* statement* RCURLY;
statement: expression? SEMICOLON
        | LCURLY data_def* statement* RCURLY
        | IF LPARENTHESIS expression RPARENTHESIS statement (ELSE statement)?
        | WHILE LPARENTHESIS expression RPARENTHESIS statement
        | BREAK SEMICOLON
        | CONTINUE SEMICOLON
        | RETURN (expression)? SEMICOLON;

expression: binary (COMMA expression)*;
binary returns [long value]: 
       id=ID ASSIGN b=binary 
       { tabla.modifyValue($id.getText(), $b.value);
        codigoSAM.append("\nLOADL 1"); 
       }
        
       | ID APLUS binary
       | ID AMULT binary
       | ID AMINUS binary
       | ID ADIV binary
       | ID AMOD binary
       | unary PLUS binary { codigoSAM.append("\nCALL (1) add"); }
       | unary MINUS binary { codigoSAM.append("\nCALL (1) sub"); }
       | unary ASTERISK binary { codigoSAM.append("\nCALL (1) mult"); }
       | unary DIV binary { codigoSAM.append("\nCALL (1) div"); }
       | unary MOD binary
       | unary AND_BIN binary
       | unary AND_BIN binary
       | unary OR_BIN binary
       | unary { $binary.value = $unary.value;} ;
       
unary returns [long value] : 
            INC ID 
            | DEC ID 
            | primary { $unary.value = $primary.value; } ;
primary returns [long value] : 
       id=ID { EntryTS entrada = tabla.find($id.getText()); $primary.value = entrada.getValue(); } // cuidado si no existe
        | n=NUMERO { $primary.value = Long.parseLong($n.getText()); } //no es CONST 
        | LPARENTHESIS expression RPARENTHESIS
        | id=ID LPARENTHESIS argument_list? RPARENTHESIS 
          {codigoSAM.append("\nCALL (SB) " + $id.getText()); };  //function_statement
        
argument_list: binary (COMMA binary)*; 
       

